/*********************************************************************************************************
 *  ------------------------------------------------------------------------------------------------------
 *  file description
 *  ------------------------------------------------------------------------------------------------------
 *         \file  str.h
 *        \brief  This is a general C language string container module
 *       \author  Lamdonn
 *        \email  Lamdonn@163.com
 *      \version  1.1.0
 *      \license  GPL2
 *    \copyright  Copyright (C) 2023 Lamdonn.
 ********************************************************************************************************/
#ifndef __str_H
#define __str_H

#include "iterator.h"
#include <stdarg.h>
#include <stdlib.h>
#include <limits.h>

/* str define */
/* all members are protected members, do not use member variables directly (let alone modify member variables), operate str_t through the given API */
typedef struct 
{
    VOBJECT parent;                     /* parent object */
    int capacity;                       /* capacity of str */
} STR, *str_t;

/* non-existent position */
#define str_npos                        INT_MAX

/** 
 *  \brief create str, it can be replaced by the `str()` macro definition method, which is more convenient
 *  \param[in] string: string, type can be array string or str_t
 *  \return handler of new str
 */
str_t str_create(void *string);

/** 
 *  \brief delete str, it can be replaced by the `_str()` macro definition method, which is more convenient
 *  \param[in] str: str handler
 *  \return none
 */
void str_delete(str_t str);

/** 
 *  \brief assign value to str
 *  \param[in] str: str handler
 *  \param[in] string: string, type can be array string or str_t
 *  \return str itself
 */
str_t str_assign(str_t str, void *string);

/** 
 *  \brief get the data actually stored in str
 *  \param[in] str: str handler
 *  \param[in] pos: position of str
 *  \return address of string data
 */
char* str_data(str_t str, int pos);

/** 
 *  \brief get the string of str
 *  \param[in] str: str handler
 *  \return address of str string
 */
const char* str_c_str(str_t str);

/** 
 *  \brief get the str error area address
 *  \return error area address
 */
const char* str_error();

/** 
 *  \brief get the length of str
 *  \param[in] str: str handler
 *  \return length of str
 */
int str_length(str_t str);

/** 
 *  \brief get the capacity of the space for storing str data
 *  \param[in] str: str handler
 *  \return capacity of str
 */
int str_capacity(str_t str);

/** 
 *  \brief judge whether the str is empty
 *  \param[in] str: str handler
 *  \return 1: empty or 0: not empty
 */
int str_empty(str_t str);

/** 
 *  \brief clear str
 *  \param[in] str: str handler
 *  \return none
 */
void str_clear(str_t str);

/** 
 *  \brief insert string to str
 *  \param[in] str: str handler
 *  \param[in] pos: insertion position
 *  \param[in] string: string, type can be array string or str_t
 *  \return str itself
 */
str_t str_insert(str_t str, int pos, void *string);

/** 
 *  \brief erase string in str
 *  \param[in] str: str handler
 *  \param[in] pos: start position of erasure
 *  \param[in] len: erased length
 *  \return str itself
 */
str_t str_erase(str_t str, int pos, int len);

/** 
 *  \brief append series string to the end of str
 *  \param[in] str: str handler
 *  \param[in] ...: strings, at least one, type can be array string or str, 
                    can be entered continuously until NULL is entered, 
                    the `str_append()` macro is used to define the method, and the NULL parameter is passed in by default
 *  \return str itself
 */
str_t str_append_series(str_t str, ...);

/** 
 *  \brief insert a character at the end of str
 *  \param[in] str: str handler
 *  \param[in] c: char
 *  \return 1: success or 0: fail
 */
int str_push_back(str_t str, char c);

/** 
 *  \brief erase a character at the end of str
 *  \param[in] str: str handler
 *  \return char of erase
 */
char str_pop_back(str_t str);

/** 
 *  \brief compare two strings
 *  \param[in] str: str handler
 *  \param[in] string: string, type can be array string or str_t
 *  \return 0: equal, -1: str < string, 1: str > string
 */
int str_compare(str_t str, void *string);

/** 
 *  \brief create substring
 *  \param[in] str: str handler
 *  \param[in] string: string, type can be array string or str_t
 *  \return new substring
 */
str_t str_substr(str_t str, int pos, int len);

/** 
 *  \brief find the specified string in str
 *  \param[in] str: str handler
 *  \param[in] string: string, type can be array string or str_t
 *  \param[in] pos: the position to start find
 *  \return find the first occurrence position of the specified string or str_npos: fail
 */
int str_find(str_t str, void *string, int pos);

/** 
 *  \brief find the specified string in str from tail
 *  \param[in] str: str handler
 *  \param[in] string: string, type can be array string or str_t
 *  \param[in] pos: the position to start find
 *  \return find the first occurrence position of the specified string or str_npos: fail
 */
int str_rfind(str_t str, void *string, int pos);

/** 
 *  \brief the first find contains the characters in the specified string
 *  \param[in] str: str handler
 *  \param[in] string: string, type can be array string or str_t
 *  \param[in] pos: the position to start find
 *  \return position of the specified character or str_npos: fail
 */
int str_find_first_of(str_t str, void *string, int pos);

/** 
 *  \brief the first find contains the characters not in the specified string
 *  \param[in] str: str handler
 *  \param[in] string: string, type can be array string or str_t
 *  \param[in] pos: the position to start find
 *  \return position of the specified character or str_npos: fail
 */
int str_find_first_not_of(str_t str, void *string, int pos);

/** 
 *  \brief the last find contains the characters in the specified string
 *  \param[in] str: str handler
 *  \param[in] string: string, type can be array string or str_t
 *  \param[in] pos: the position to start find
 *  \return position of the specified character or str_npos: fail
 */
int str_find_last_of(str_t str, void *string, int pos);

/** 
 *  \brief the last find contains the characters not in the specified string
 *  \param[in] str: str handler
 *  \param[in] string: string, type can be array string or str_t
 *  \param[in] pos: the position to start find
 *  \return position of the specified character or str_npos: fail
 */
int str_find_last_not_of(str_t str, void *string, int pos);

/** 
 *  \brief format str
 *  \param[in] str: str handler
 *  \param[in] begin: begin position
 *  \param[in] end: end position
 *  \return str itself
 */
str_t str_reverse(str_t str, int begin, int end);

/** 
 *  \brief str replace
 *  \param[in] str: str handler
 *  \param[in] pos: start position of be replace
 *  \param[in] len: erased length
 *  \param[in] string: string, type can be array string or str_t
 *  \return str itself
 */
str_t str_replace(str_t str, int pos, int len, void *string);

/** 
 *  \brief swap two str
 *  \param[in] str: str handler
 *  \param[in] swap: str handler of swap
 *  \return none
 */
void str_swap(str_t str, str_t swap);

/** 
 *  \brief swap two str
 *  \param[in] str: str handler
 *  \param[in] pos: start position of be copy
 *  \param[in] len: copy length
 *  \param[out] buf: array copied
 *  \return number of characters actually copied
 */
int str_copy(str_t str, int pos, int len, char *buf);

/** 
 *  \brief format str
 *  \param[in] str: str handler
 *  \param[in] format: format
 *  \param[in] ...: indefinite parameter
 *  \return str itself
 */
str_t str_format(str_t str, const char *format, ...);

/** 
 *  \brief macro definition method for creating str
 *  \param[in] str: string, type can be array string or str_t
 *  \return str handler
 */
#define str(str)                        str_create(str)

/** 
 *  \brief macro definition method for delete str
 *  \param[in] str: str handler
 *  \return none
 */
#define _str(str)                       do{str_delete(str);(str)=NULL;}while(0)

/** 
 *  \brief append series string to the end of str
 *  \param[in] str: str handler
 *  \param[in] ...: strings, at least one, type can be array string or str_t
 *  \return str itself
 */
#define str_append(str, ...)            str_append_series((str), ##__VA_ARGS__, NULL)

/** 
 *  \brief macro definition method for get char of str
 *  \param[in] str: str handler
 *  \param[in] i: index of string
 *  \return reference to the specified character
 */
#define str_at(str, i)                  (*str_data((str),(i)))

/** 
 *  \brief macro definition method for get front char of str
 *  \param[in] str: str handler
 *  \return reference to the front char
 */
#define str_front(str)                  str_at(str, 0)

/** 
 *  \brief macro definition method for get back char of str
 *  \param[in] str: str handler
 *  \return reference to the back char
 */
#define str_back(str)                   str_at(str, str_length(str)-1)

/** 
 *  \brief multiplexing `str_c_str()`, get the string of str
 *  \param[in] str: str handler
 *  \return reference to the back char
 */
#define _S(str)                         str_c_str(str)

#endif
